package com.matthicks.macrobot;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.SocketException;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.prefs.Preferences;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.BadLocationException;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
import javax.swing.text.TabSet;
import javax.swing.text.TabStop;
import javax.swing.undo.UndoManager;

import bsh.EvalError;
import bsh.Interpreter;

public class Macrobot {
	private static final Macrobot instance = new Macrobot();
	
	private Commander commander;
	
	private Preferences preferences;
	
	public static PrintStream originalOut;
	public static PrintStream originalErr;
	
	private JFrame frame;
	private JLabel status;
	private JProgressBar progressBar;
	private JTextPane editor;
	private JTextPane log;
	private JTextField input;
	private StyledDocument document;
	private JSplitPane split;
	
	private Style consoleStyle;
	private Style outputStyle;
	private Style errorStyle;
	private Style codeStyle;
	private Style linkStyle;
	
	private ExecutionProgress progress;
	
	private Interpreter interpreter;
	
	private List<String> commands;
	private List<Command> history;
	private int historyPosition;
	
	private UndoManager undo = new UndoManager();
	
	private boolean closed;
	
	private Macrobot() {
		preferences = Preferences.userNodeForPackage(getClass());
		
		historyPosition = -1;
		history = new LinkedList<Command>();
		
		commander = Controller.get();
		
		progress = new ExecutionProgress();
		
		reset();
	}
	
	public ExecutionProgress getProgress() {
		return progress;
	}
	
	public void initializeUI() throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException {
		UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
		
		progress.setReceiver(new Runnable() {
			public void run() {
				updateUIProgress();
			}
		});
		
		frame = new JFrame("Macrobot"); {
			frame.addWindowListener(new WindowListener() {
				public void windowActivated(WindowEvent e) {
				}

				public void windowClosed(WindowEvent e) {
				}

				public void windowClosing(WindowEvent e) {
					close();
				}

				public void windowDeactivated(WindowEvent e) {
				}

				public void windowDeiconified(WindowEvent e) {
				}

				public void windowIconified(WindowEvent e) {
				}

				public void windowOpened(WindowEvent e) {
				}
			});
			int w = preferences.getInt("uiW", 800);
			int h = preferences.getInt("uiH", 600);
			frame.setSize(w, h);
			int x = preferences.getInt("uiX", 0);
			int y = preferences.getInt("uiY", 0);
			frame.setLocation(x, y);
			
			KeyListener kl = new KeyListener() {
				public void keyPressed(KeyEvent e) {
				}

				public void keyReleased(KeyEvent e) {
					if (e.getKeyCode() == KeyEvent.VK_F5) {
						execute();
					}
				}

				public void keyTyped(KeyEvent e) {
				}
			};
			
			JPanel panel = new JPanel(); {
				panel.setLayout(new BorderLayout());
				
				JPanel buttons = new JPanel(); {
					buttons.setLayout(new FlowLayout());
					
					JButton button = new JButton("Execute"); {
						button.addActionListener(new ActionListener() {
							public void actionPerformed(ActionEvent e) {
								execute();
							}
						});
						buttons.add(button);
					}
					button = new JButton("Load Script"); {
						button.addActionListener(new ActionListener() {
							public void actionPerformed(ActionEvent e) {
								loadScript();
							}
						});
						buttons.add(button);
					}
					button = new JButton("Save Script"); {
						button.addActionListener(new ActionListener() {
							public void actionPerformed(ActionEvent e) {
								saveScript();
							}
						});
						buttons.add(button);
					}
					button = new JButton("Show Commands"); {
						button.addActionListener(new ActionListener() {
							public void actionPerformed(ActionEvent e) {
								showCommands();
							}
						});
						buttons.add(button);
					}
					button = new JButton("Reset"); {
						button.addActionListener(new ActionListener() {
							public void actionPerformed(ActionEvent e) {
								reset();
							}
						});
						buttons.add(button);
					}
					button = new JButton("Exit"); {
						button.addActionListener(new ActionListener() {
							public void actionPerformed(ActionEvent e) {
								try {
									close();
								} catch (Exception e1) {
									e1.printStackTrace();
								}
							}
						});
						buttons.add(button);
					}
					status = new JLabel("Local"); {
						status.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 5));
						status.setFont(new Font("Arial", Font.BOLD, 16));
						status.setForeground(new Color(0.0f, 0.6f, 0.0f));
						buttons.add(status);
					}
					
					progressBar = new JProgressBar(0, 100); {
						progressBar.setString("Idle");
						buttons.add(progressBar);
					}
					
					panel.add(buttons, BorderLayout.NORTH);
				}
				
				split = new JSplitPane(JSplitPane.VERTICAL_SPLIT); {
					editor = new JTextPane(); {
						editor.addKeyListener(kl);
						editor.addKeyListener(new KeyListener() {
							public void keyPressed(KeyEvent e) {
							}

							public void keyReleased(KeyEvent e) {
								if (e.isControlDown()) {
									if (e.getKeyCode() == KeyEvent.VK_Z) {
										// Undo
										if (undo.canUndo()) {
											undo.undo();
										}
									} else if (e.getKeyCode() == KeyEvent.VK_Y) {
										// Redo
										if (undo.canRedo()) {
											undo.redo();
										}
									}
								}
							}

							public void keyTyped(KeyEvent e) {
							}
						});
						editor.setFont(new Font("monospaced", Font.PLAIN, 12));
						editor.getStyledDocument().addUndoableEditListener(new UndoableEditListener() {
							public void undoableEditHappened(UndoableEditEvent e) {
								undo.addEdit(e.getEdit());
							}
						});
						setTabs(editor, 4);
						File f = new File("editor.content");
						if (f.exists()) {
							try {
								FileInputStream fis = new FileInputStream(f);
								byte[] buf = new byte[(int)f.length()];
								fis.read(buf);
								String savedContent = new String(buf);
								editor.setText(savedContent);
								fis.close();
							} catch (Throwable t) {
								t.printStackTrace();
							}
						}
//						String savedContent = new String(preferences.getByteArray("editorContent", new byte[0]));
//						editor.setText(savedContent);
					}
					
					JScrollPane editorScroll = new JScrollPane(editor); {
						editorScroll.getVerticalScrollBar().setUnitIncrement(10);
						split.setLeftComponent(editorScroll);
					}
					
					JPanel logPanel = new JPanel(); {
						logPanel.setBackground(Color.WHITE);
						logPanel.setLayout(new BorderLayout());
						
						log = new JTextPane(); {
							log.setEditable(false);
							document = log.getStyledDocument();
							defineStyles();
							redirectOutput();
						}
						
						logPanel.add(log, BorderLayout.SOUTH);
					}
					
					JScrollPane scroll = new JScrollPane(logPanel); {
						scroll.getVerticalScrollBar().setUnitIncrement(30);
						split.setRightComponent(scroll);
					}
					
					int dividerLocation = preferences.getInt("divider", 300);
					split.setDividerLocation(dividerLocation);
					panel.add(split, BorderLayout.CENTER);
				}
				
				input = new JTextField(); {
					input.addKeyListener(kl);
					input.addKeyListener(new KeyListener() {
						public void keyPressed(KeyEvent e) {
						}

						public void keyReleased(KeyEvent e) {
							if (e.getKeyCode() == KeyEvent.VK_ENTER) {
								submit();
							} else if (e.getKeyCode() == KeyEvent.VK_UP) {
								historyPosition++;
								if (historyPosition == history.size()) {
									historyPosition--;
								}
								showCurrentHistory();
							} else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
								historyPosition--;
								if (historyPosition < -1) {
									historyPosition = -1;
								}
								showCurrentHistory();
							}
						}

						public void keyTyped(KeyEvent e) {
						}
					});
					
					panel.add(input, BorderLayout.SOUTH);
				}
				
				frame.setContentPane(panel);
			}
		}
	}
	
	public void updateUIProgress() {
		if (progress.getPercent() != null) {
			progressBar.setValue(progress.getPercent());
			progressBar.setString(progress.getStatus());
		} else {
			progressBar.setValue(0);
		}
	}
	
	public void loadScript() {
		File directory = new File(preferences.get("directory", "."));
		JFileChooser chooser = new JFileChooser(directory);
		chooser.setFileFilter(new FileFilter() {
			public String getDescription() {
				return "Macrobot Scripts";
			}
			
			public boolean accept(File f) {
				return f.getName().toLowerCase().endsWith(".script");
			}
		});
		int response = chooser.showOpenDialog(frame);
		if (response == JFileChooser.APPROVE_OPTION) {
			File file = chooser.getSelectedFile();
			directory = file.getParentFile();
			if (directory != null) {
				preferences.put("directory", directory.getAbsolutePath());
			}
			try {
				System.out.println("Loading script from " + file.getCanonicalPath());
				BufferedReader reader = new BufferedReader(new FileReader(file));
				StringBuilder b = new StringBuilder();
				String line;
				while((line = reader.readLine()) != null) {
					b.append(line);
					b.append("\n");
				}
				reader.close();
				editor.setText(b.toString());
				System.out.println("File loaded successfully.");
			} catch(Throwable t) {
				t.printStackTrace();
			}
		}
	}
	
	public void saveScript() {
		File directory = new File(preferences.get("directory", "."));
		JFileChooser chooser = new JFileChooser(directory);
		chooser.setFileFilter(new FileFilter() {
			public String getDescription() {
				return "Macrobot Scripts";
			}
			
			public boolean accept(File f) {
				return f.getName().toLowerCase().endsWith(".script");
			}
		});
		int response = chooser.showSaveDialog(frame);
		if (response == JFileChooser.APPROVE_OPTION) {
			File file = chooser.getSelectedFile();
			directory = file.getParentFile();
			if (directory != null) {
				preferences.put("directory", directory.getAbsolutePath());
			}
			try {
				System.out.println("Saving to script to " + file.getCanonicalPath());
				BufferedWriter writer = new BufferedWriter(new FileWriter(file));
				writer.write(editor.getText());
				writer.flush();
				writer.close();
				System.out.println("File saved successfully.");
			} catch(Throwable t) {
				t.printStackTrace();
			}
		}
	}
	
	public void showCommands() {
		System.out.println("Commands:");
		for (String s : commands) {
			System.out.println("\t" + s);
		}
	}
	
	@SuppressWarnings("unchecked")
	public void reset() {
		try {
			byte[] historyBytes = preferences.getByteArray("history", null);
			if (historyBytes != null) {
				ByteArrayInputStream bais = new ByteArrayInputStream(historyBytes);
				ObjectInputStream ois = new ObjectInputStream(bais);
				history = (List<Command>)ois.readObject();
				historyPosition = -1;
			}
			
			interpreter = new Interpreter();
			interpreter.eval("import com.matthicks.macrobot.*;");
			interpreter.eval("import java.awt.image.*;");
			interpreter.set("c", commander);
			interpreter.set("view", this);
			interpreter.set("progress", progress);
			
			// Process startup scripts
			File directory = new File("startup");
			if (directory.isDirectory()) {
				for (File f : directory.listFiles()) {
					if (f.getName().endsWith(".script")) {
						executeScriptFile(f);
					}
				}
			}
			
			Method[] methods = Controller.class.getMethods();
			commands = new ArrayList<String>();
			for (Method m : methods) {
				if ((m.getModifiers() & Modifier.STATIC) == 0) {
					StringBuilder builder = new StringBuilder();
					builder.append("public ");
					StringBuilder tmp = new StringBuilder();
					tmp.append(m.getReturnType().getCanonicalName());
					tmp.append(' ');
					tmp.append(m.getName());
					tmp.append('(');
					for (int i = 0; i < m.getParameterTypes().length; i++) {
						if (i > 0) {
							tmp.append(", ");
						}
						tmp.append(m.getParameterTypes()[i].getCanonicalName());
						tmp.append(" arg");
						tmp.append(i);
					}
					tmp.append(')');
					commands.add(tmp.toString());
					builder.append(tmp);
					builder.append(" {\n");
					builder.append("\tCommand command = new Command();\n");
					builder.append("\tcommand.name = \"" + m.getName() + "\";\n");
					builder.append("\tString[] args = new String[" + m.getParameterTypes().length + "];\n");
					for (int i = 0; i < m.getParameterTypes().length; i++) {
						builder.append("\targs[" + i + "] = String.valueOf(arg" + i + ");\n");
					}
					builder.append("\tcommand.args = args;\n");
					builder.append("\tCommandResponse response = c.processCommand(command);\n");
					if (m.getReturnType() != void.class) {
						builder.append("\treturn (" + m.getReturnType().getCanonicalName() + ")response.payload;\n");
					}
					builder.append("}");
					interpreter.eval(builder.toString());
				}
			}
		} catch(Throwable t) {
			t.printStackTrace();
		}
	}
	
	public void setTabs(JTextPane textPane, int charactersPerTab) {
		FontMetrics fm = textPane.getFontMetrics( textPane.getFont() );
		int charWidth = fm.charWidth( 'w' );
		int tabWidth = charWidth * charactersPerTab;
 
		TabStop[] tabs = new TabStop[10];
 
		for (int j = 0; j < tabs.length; j++)
		{
			int tab = j + 1;
			tabs[j] = new TabStop( tab * tabWidth );
		}
 
		TabSet tabSet = new TabSet(tabs);
		SimpleAttributeSet attributes = new SimpleAttributeSet();
		StyleConstants.setTabSet(attributes, tabSet);
		int length = textPane.getDocument().getLength();
		textPane.getStyledDocument().setParagraphAttributes(0, length, attributes, true);
	}
	
	private void showCurrentHistory() {
		if (historyPosition >= 0) {
			input.setText(history.get(historyPosition).toString());
			input.setSelectionStart(0);
			input.setSelectionEnd(input.getText().length());
		} else {
			input.setText("");
		}
	}
	
	private void defineStyles() {
		Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
		Style regular = document.addStyle("regular", def);
        StyleConstants.setFontFamily(def, "SansSerif");
        
		consoleStyle = document.addStyle("console", regular);
        StyleConstants.setBold(consoleStyle, true);
        StyleConstants.setForeground(consoleStyle, new Color(0.6f, 0.8f, 1.0f));
        
        outputStyle = document.addStyle("output", regular);
        StyleConstants.setForeground(outputStyle, Color.BLUE);
        
        errorStyle = document.addStyle("error", regular);
        StyleConstants.setForeground(errorStyle, Color.RED);
        
        codeStyle = document.addStyle("code", regular);
        StyleConstants.setFontFamily(codeStyle, "Courier");
        
        Style codeStyle1 = document.addStyle("code1", codeStyle);
        StyleConstants.setForeground(codeStyle1, new Color(0.9f, 0.7f, 0.7f));
        
        Style codeStyle2 = document.addStyle("code2", codeStyle);
        StyleConstants.setForeground(codeStyle2, new Color(0.7f, 0.9f, 0.7f));
        
        Style heading = document.addStyle("heading", regular);
        StyleConstants.setForeground(heading, new Color(0.6f, 1.0f, 1.0f));
        StyleConstants.setBold(heading, true);
        
        linkStyle = document.addStyle("link", regular);
        StyleConstants.setUnderline(linkStyle, true);
        StyleConstants.setBold(linkStyle, true);
	}
	
	public void start() {
		frame.setVisible(true);
		
		input.requestFocus();
	}
	
	public void write(String style, String text) {
		write(document.getStyle(style), text);
	}
	
	public void write(Style style, String text) {
		try {
			document.insertString(document.getLength(), text, style);
			log.setCaretPosition(document.getLength());
		} catch(Exception exc) {
			exc.printStackTrace();
		}
	}
	
	public void writeLine(String style, String text) {
		write(style, text + "\n");
	}
	
	public void writeLine(Style style, String text) {
		write(style, text + "\n");
	}
	
	private int linkInc = 0;
	public void writeLink(String text, String link) {
		Style style = document.addStyle("link" + linkInc++, linkStyle);
		style.addAttribute("link", link);
		write(style, text);
	}
	
	private void redirectOutput() {
		originalOut = System.out;
		originalErr = System.err;
		
		StyledOutputStream sos = new StyledOutputStream(document, outputStyle);
		System.setOut(new PrintStream(sos));
		sos = new StyledOutputStream(document, errorStyle);
		System.setErr(new PrintStream(sos));
	}
	
	private void submit() {
		Thread t = new Thread() {
			public void run() {
				String text = input.getText().trim();
				if (text.length() > 0) {
					try {
						Command command = new Command(text);
						if (command.name.equals("clear")) {
							clear();
						} else if (command.name.equals("connect")) {
							System.out.println("Attempting to connect...");
							String host = null;
							int port = 0;
							if (command.args.length == 1) {
								port = Integer.parseInt(command.args[0]);
							} else {
								host = command.args[0];
								port = Integer.parseInt(command.args[1]);
							}
							connect(host, port);
						} else if (command.name.equals("disconnect")) {
							disconnect();
						} else if (command.name.equals("execute")) {
							execute();
						} else {
							CommandResponse response = commander.processCommand(command);
							if (response.successful) {
								writeLine(outputStyle, command.toString());
							}
							if (response.payload instanceof BufferedImage) {
								BufferedImage image = null;
								if (response.payload instanceof BufferedImage) {
									image = (BufferedImage)response.payload;
								} else {
									ByteArrayInputStream bais = new ByteArrayInputStream((byte[])response.payload);
									image = ImageIO.read(bais);
								}
								draw(image);
							}
						}
						input.setText("");
						history.add(0, command);
						while (history.size() > 50) {
							history.remove(history.size() - 1);
						}
						System.out.println("History size: " + history.size());
						historyPosition = -1;
						try {
							ByteArrayOutputStream baos = new ByteArrayOutputStream();
							ObjectOutputStream oos = new ObjectOutputStream(baos);
							oos.writeObject(history);
							oos.flush();
							
							byte[] historyBytes = baos.toByteArray();
							
							preferences.putByteArray("history", historyBytes);
						} catch(Exception exc) {
							exc.printStackTrace();
						}
					} catch(SocketException exc) {
						System.err.println("Connection Lost:");
						exc.printStackTrace();
						disconnect();
					} catch(Exception exc) {
						exc.printStackTrace();
					}
				}
			}
		};
		t.start();
	}
	
	public void connect(String host, int port) throws UnknownHostException, IOException, EvalError {
		CommandSender sender = new CommandSender(host, port);
		commander = sender;
		interpreter.set("c", commander);
		if (status != null) {
			status.setText("Online: " + (host != null ? host + ":" + port : port));
		}
		System.out.println("Changed command mode to remote.");
	}
	
	public void disconnect() {
		if (commander instanceof CommandSender) {
			try {
				CommandSender sender = (CommandSender)commander;
				sender.close();
			} catch(Throwable t) {
				t.printStackTrace();
			}
			commander = Controller.get();
			try {
				interpreter.set("c", commander);
			} catch(Throwable t) {
				t.printStackTrace();
			}
			if (status != null) {
				status.setText("Local");
			}
			System.out.println("Disconnected, returning to local command mode.");
		} else {
			System.err.println("Not connected.");
		}
	}
	
	public void clear() throws BadLocationException {
		document.remove(0, document.getLength());
		log.setCaretPosition(document.getLength());
	}
	
	public void draw(BufferedImage image) {
		ImageIcon icon = new ImageIcon(image);
		Style style = document.addStyle("testStyle", null);
		StyleConstants.setIcon(style, icon);
		writeLine(style, "");
	}
	
	public BufferedImage clip(BufferedImage image, int x, int y, int width, int height) {
		BufferedImage response = image.getSubimage(x, y, width, height);
		draw(response);
		return response;
	}
	
	public void invokeCommand(Command command) throws Exception {
		commander.processCommand(command);
	}
	
	public void execute() {
		saveState();				// Save state before executing each time
		Thread t = new Thread() {
			public void run() {
				String content = editor.getSelectedText();
				if ((content == null) || (content.trim().length() == 0)) {
					content = editor.getText();
				}
				try {
					executeScript(content);
				} catch(Throwable t) {
					t.printStackTrace();
				}
			}
		};
		t.start();
	}
	
	public void executeScript(String script) throws EvalError {
//		try {
			long time = System.nanoTime();
			progress.set(0, "Executing...");
			try {
				interpreter.eval(script);
				System.out.println("Executed successfully in " + ((System.nanoTime() - time) / 1000000));
			} finally {
				progress.set(null, null);
			}
//		} catch(Throwable t) {
//			t.printStackTrace();
//		}
	}
	
	public void executeScriptURL(URL url) throws IOException, EvalError {
		executeScriptStream(url.openStream());
	}
	
	public void executeScriptFile(File file) throws IOException, EvalError {
		executeScriptStream(new FileInputStream(file));
	}
	
	public void executeScriptStream(InputStream input) throws IOException, EvalError {
		BufferedReader reader = new BufferedReader(new InputStreamReader(input));
		StringBuilder b = new StringBuilder();
		String line;
		while ((line = reader.readLine()) != null) {
			b.append(line);
			b.append("\n");
		}
		input.close();
		executeScript(b.toString());
	}
	
	public void close() {
		if (!closed) {
			closed = true;
			System.setOut(originalOut);
			System.setErr(originalErr);
			saveState();
			frame.dispose();
		}
	}
	
	public void saveState() {
		File f = new File("editor.content");
		try {
			FileOutputStream fos = new FileOutputStream(f);
			fos.write(editor.getText().getBytes());
			fos.flush();
			fos.close();
		} catch (Throwable t) {
			t.printStackTrace();
		}
//		preferences.putByteArray("editorContent", editor.getText().getBytes());
//		preferences.put("editorContent", editor.getText());
		preferences.putInt("divider", split.getDividerLocation());
		preferences.putInt("uiX", frame.getLocation().x);
		preferences.putInt("uiY", frame.getLocation().y);
		preferences.putInt("uiW", frame.getSize().width);
		preferences.putInt("uiH", frame.getSize().height);
	}
	
	public void dispose() {
		disconnect();
	}
	
	public static final synchronized Macrobot get() {
		return instance;
	}
	
	public static void main(String[] args) throws Exception {
		String host = null;
		int port = 2600;
		String script = null;
		for (int i = 0; i < args.length; i++) {
			String arg = args[i];
			if ("-port".equals(arg)) {
				i++;
				port = Integer.parseInt(args[i]);
			} else if ("-host".equals(arg)) {
				i++;
				host = args[i];
			} else if ("-script".equals(arg)) {
				i++;
				script = args[i];
			}
		}
		if (args.length == 0) {
			Macrobot m = Macrobot.get();
			m.initializeUI();
			m.start();
		} else if (script != null) {
			Macrobot m = Macrobot.get();
			if (host != null) {
				m.connect(host, port);
			}
			m.executeScriptFile(new File(script));
		} else {
			CommandListener listener = new CommandListener(host, port);
			listener.start();
		}
	}
}